/*
 * REFObject.h
 *
 *  Created on: Jan 23, 2013
 *      Author: Lucifer
 */

#ifndef __BFX_REFOBJECT_H__
#define __BFX_REFOBJECT_H__

namespace BFX
{

//////////////////////////////////////////////////////////////////////////////

// The base class for the referenceable objects (REF) library.
class BFX_API REFObject
{
public:
	//
	// Constructions
	//
	REFObject() :
			m_lRefCount(0)
	{
	}
	virtual ~REFObject()
	{
	}

	//
	// Operations
	//

	//Returns the reference count.
	long GetRefCount() const
	{
		return m_lRefCount;
	}

	//
	// Overrides
	//
	virtual bool IsValid() const
	{
		return true;
	}
	// Increments the reference count.
	virtual long AddRef() const
	{
		return Interlocked::Increment(&m_lRefCount);
		// return ++m_lRefCount;
	}
	// Decrements the reference count.
	virtual long Release() const
	{
		if (Interlocked::Decrement(&m_lRefCount) <= 0)
		// if (--m_lRefCount <= 0)
		{
			BFX_ASSERT(m_lRefCount == -1 || m_lRefCount == 0);
			delete this;
			return 0;
		}
		return m_lRefCount;
	}
	// Decrements the reference count at the end of the current auto-release pool block.
	virtual void Autorelease() const;

	//
	// Attributes
	//
private:
	mutable long m_lRefCount;
};

//////////////////////////////////////////////////////////////////////////////

template<class T>
class BFX_TMPL REF
{
private:
	T* m_pt;

	//
	// Constructions
	//
public:
	REF() :
			m_pt(NULL)
	{
	}
	REF(const T* pt)
	{
		m_pt = (T*) pt;
		if (m_pt)
			m_pt->AddRef();
	}
	REF(const REF& ref) :
			m_pt(ref.m_pt)
	{
		if (m_pt)
			m_pt->AddRef();
	}
	~REF(void)
	{
		if (m_pt)
			m_pt->Release();
	}

	//
	// Assignment
	//
	REF* Pointer()
	{
		return this;
	}
	T** operator&()
	{
		if (m_pt)
		{
			m_pt->Release();
			m_pt = NULL;
		}
		return &m_pt;
	}
	REF& operator=(const REF& ref)
	{
		T* ptOld = m_pt;
		m_pt = ref.m_pt;

		if (m_pt)
			m_pt->AddRef();

		if (ptOld)
			ptOld->Release();

		return *this;
	}
	REF& operator=(const T* ptNew)
	{
		T* ptOld = m_pt;
		m_pt = (T*) ptNew;

		if (m_pt)
			m_pt->AddRef();

		if (ptOld)
			ptOld->Release();

		return *this;
	}

	//
	// Members
	//
	operator T*(void) const
	{
		return m_pt;
	}
	T& operator*(void) const
	{
		return *m_pt;
	}
	T* operator->(void) const
	{
		return m_pt;
	}

	friend bool operator==(const REF& t1, T* pt)
	{
		return t1.m_pt == pt;
	}
	friend bool operator!=(const REF& t1, T* pt)
	{
		return t1.m_pt != pt;
	}

	// define comparison operators for use in associative containers
	friend bool operator<(const REF& t1, T* pt)
	{
		return t1.m_pt < pt;
	}
	;
	friend bool operator>(const REF& t1, T* pt)
	{
		return t1.m_pt > pt;
	}
	;
	friend bool operator<=(const REF& t1, T* pt)
	{
		return t1.m_pt <= pt;
	}
	;
	friend bool operator>=(const REF& t1, T* pt)
	{
		return t1.m_pt >= pt;
	}
};

//////////////////////////////////////////////////////////////////////////////

template<typename T>
class REFWrapper : public T, virtual public REFObject
{
};

}	//	namespace BFX

//////////////////////////////////////////////////////////////////////////////
// special versions of HashTraits for REFObject.

#include "../common/HashTraits.h"

namespace BFX
{
template<class T>
class HashTraits<REF<T> >
{
public:
	static int __cdecl GetHashCode(T* pObj) throw()
	{
		typedef HashTraits<void*> PtrHashTraits;
		if (pObj == NULL)
			return 0;	// NOTE: NULL value is also allowed.
		return (int) PtrHashTraits::GetHashCode(pObj);
	}
	static bool __cdecl Equals(T* pObj1, T* pObj2) throw()
	{
		if (pObj1 == NULL || pObj2 == NULL)
			return pObj1 == pObj2;
		return (pObj1 == pObj2);
	}
};


} /* namespace BFX */

#endif /* __BFX_REFOBJECT_H__ */
